package com.alibaba.citrus.cr.sales.plan.facade.service.salesplan.publish;

import com.alibaba.citrus.cr.common.annotation.EpochTransactional;
import com.alibaba.citrus.cr.common.annotation.FacadeInvoker;
import com.alibaba.citrus.cr.common.exception.ActivityException;
import com.alibaba.citrus.cr.common.exception.FacadeException;
import com.alibaba.citrus.cr.common.lock.RedisLock;
import com.alibaba.citrus.cr.common.util.CalculatePercentage;
import com.alibaba.citrus.cr.sales.plan.api.setting.SalesPlanStrategyDetailQueryService;
import com.alibaba.citrus.cr.sales.plan.contants.SalesPlanLockKey;
import com.alibaba.citrus.cr.sales.plan.dto.api.response.SalesPlanStrategyDetailResponse;
import com.alibaba.citrus.cr.sales.plan.model.SalesPlanLineStatusEnum;
import com.alibaba.citrus.cr.sales.plan.model.enums.SalesPlanAdjustCheckStatusEnum;
import com.alibaba.citrus.cr.sales.plan.model.enums.SalesPlanCheckStatusEnum;
import com.alibaba.citrus.cr.sales.plan.model.enums.SalesPlanModelEnum;
import com.alibaba.citrus.cr.sales.plan.model.enums.SalesPlanStatusEnum;
import com.alibaba.citrus.cr.sales.plan.model.enums.SalesStatBizTypeEnum;
import com.alibaba.citrus.cr.sales.plan.model.enums.StatPoolTypeEnum;
import com.alibaba.citrus.cr.sales.plan.repository.SalesPlanAdjustDetailRepository;
import com.alibaba.citrus.cr.sales.plan.repository.SalesPlanLineRepository;
import com.alibaba.citrus.cr.sales.plan.repository.SalesPlanRepository;
import com.alibaba.citrus.cr.sales.plan.repository.SalesPlanScItemExtendRepository;
import com.alibaba.citrus.cr.sales.plan.repository.SalesPlanScItemStatRepository;
import com.alibaba.citrus.cr.sales.plan.sdo.SalesPlanAdjustDetailSDO;
import com.alibaba.citrus.cr.sales.plan.sdo.SalesPlanLineSDO;
import com.alibaba.citrus.cr.sales.plan.sdo.SalesPlanSDO;
import com.alibaba.citrus.cr.sales.plan.sdo.SalesPlanScItemExtendSDO;
import com.alibaba.citrus.cr.sales.plan.sdo.SalesPlanStatScItemSDO;
import com.alibaba.citrus.cr.sales.plan.sdo.SalesPlanStrategyDetailSDO;
import com.alibaba.citrus.cr.sales.plan.api.salesplan.publish.SalesPlanPublishWriteServiceNew;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.DispatchPlanScItemRequest;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.PageQueryPlanLineRequest;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.QueryPlanScItemExtendRequest;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.QueryPlanScItemStatRequest;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.QuerySalesPlanRequest;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.QuerySalesPlanStrategyListRequest;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.SalesOrgRatioDTO;
import com.alibaba.citrus.cr.sales.plan.dto.api.request.SalesPlanAdjustDetailBatchQueryRequest;
import com.alibaba.citrus.cr.sales.plan.facade.service.SalesPlanCommonService;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.cz.base.tool.page.PageResult;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.middleware.mq.runtime.EpochMessageService;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.epoch.app.bcorder.contants.BcOrderConstants;
import com.epoch.app.bcorder.model.dto.BatchDeletePlanScItemExtendRequest;
import com.epoch.app.bcorder.service.SalesPlanService;
import com.epoch.app.crplatformenhance.api.organizationquery.service.OrganizationQueryService;
import com.epoch.app.crplatformenhance.dto.request.IdQueryRequest;
import com.epoch.app.crplatformenhance.enums.OrganizationTypeEnum;
import com.epoch.app.crplatformenhance.model.dto.GetDownOrganizationListRequest;
import com.epoch.app.crplatformenhance.sdo.OrganizationSDO;
import com.epoch.app.crplatformenhance.service.CrPlatformEnhanceService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;


@Service
public class SalesPlanPublishWriteServiceNewImpl implements SalesPlanPublishWriteServiceNew {

    private static final Log log = Log.getLogger(SalesPlanPublishWriteServiceNewImpl.class);

    @Resource
    private SalesPlanRepository salesPlanRepository;

    @Resource
    private SalesPlanScItemExtendRepository salesPlanScItemExtendRepository;

    @Resource
    private SalesPlanScItemStatRepository salesPlanScItemStatRepository;

    @Resource
    private SalesPlanLineRepository salesPlanLineRepository;

    @Resource
    private SalesPlanCommonService salesPlanCommonService;

    @Resource
    private SalesPlanStrategyDetailQueryService salesPlanStrategyDetailQueryService;

    @Resource
    private SalesPlanAdjustDetailRepository salesPlanAdjustDetailRepository;

    @Resource
    private EpochMessageService messageService;

    @Resource
    private SalesPlanService salesPlanService;

    @Resource
    private OrganizationQueryService organizationQueryService;

    private static final Integer DEFAULT_PAGE_SIZE = 1000;

    /**
     * 生成部门回复量
     * <p>
     * // 0、基本参数校验
     * // 1、月度计划校验（校验存在和状态处于待发布状态）
     * // 2、查询业务单元的货品回复量
     * // 3、业务单元的货品回复量和提报量比较（供大于求，按原提报量回复部门回复量和任务量）
     * // 4、求大于供的情况
     * //   4.1 查询业务单元下的月度计划策略
     * //   4.2 根据销售组织策略占比，计算销售组织的回复量和任务量
     * //   4.3 在销售组织下，根据下属部门策略，循环计算部门的回复量和任务量
     * // 5、批量添加到extend表中
     *
     * @param dispatchPlanScItemRequest
     * @return
     */
    @Override
    @FacadeInvoker
    public Result<Boolean> dispatchPlanScItemForDepartment(DispatchPlanScItemRequest dispatchPlanScItemRequest) {
        Boolean aBoolean = true;
        try (RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(), SalesPlanLockKey.getPlanPublishDepartmentLockKey(dispatchPlanScItemRequest.getPlanId()))) {
            boolean locked = lock.lock();
            if (!locked) {
                log.error("SalesPlanStatServiceImpl#dispatchPlanScItemForDepartment_getLock_fail planId=" + dispatchPlanScItemRequest.getPlanId());
                return Result.fail("OTS-02-006-00-16-006", "部门任务量正在生成中，请稍后重试");
            }
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            aBoolean = dispatchPlanScItemForDepartmentNew(dispatchPlanScItemRequest);
            stopWatch.stop();
            log.info("SalesPlanPublishWriteServiceNewImpl_dispatchPlanScItemForDepartment_planId="+dispatchPlanScItemRequest.getPlanId()+" elapsed time:"+stopWatch.getTotalTimeMillis()+"ms");
        } catch (FacadeException e) {
            log.error("SalesPlanPublishWriteServiceImpl#dispatchPlanScItemForDepartment_fail ", e);
            throw new FacadeException(e.getErrorCode(), e.getErrorMessage());
        }
        return Result.success(aBoolean);
    }

    @Override
    public Result<Boolean> dispatchPlanScItemForCustomer(DispatchPlanScItemRequest dispatchPlanScItemRequest) {
        try (RedisLock lock = new RedisLock(BcOrderConstants.REDIS_CACHE_CODE(), SalesPlanLockKey.getDispatchPlanScItemForCustomerLockKey(dispatchPlanScItemRequest.getPlanId()))) {
            boolean locked = lock.lock();
            if (!locked) {
                log.error("SalesPlanPublishWriteServiceImpl_dispatchPlanScItemForCustomer_getLock_fail planId=" + dispatchPlanScItemRequest.getPlanId());
                return Result.fail("OTS-02-006-00-16-006", "正在生成客户回复量中，请稍后重试");
            }
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            dispatchPlanScItemForCustomerNew(dispatchPlanScItemRequest);
            stopWatch.stop();
            log.info("SalesPlanPublishWriteServiceImpl_dispatchPlanScItemForCustomer_planId="+dispatchPlanScItemRequest.getPlanId()+" elapsed time:"+stopWatch.getTotalTimeMillis()+"ms");
        } catch (FacadeException e) {
            log.error("SalesPlanPublishWriteServiceImpl#dispatchPlanScItemForCustomer_fail ", e);
            throw new FacadeException(e.getErrorCode(), e.getErrorMessage());
        }
        return Result.success(true);
    }

    /**
     * 生产客户回复量
     * @param dispatchPlanScItemRequest
     */
    @Override
    public void dispatchPlanScItemForCustomerNew(DispatchPlanScItemRequest dispatchPlanScItemRequest) {
        // 0、相关参数校验
        checkParam(dispatchPlanScItemRequest);
        String planId = dispatchPlanScItemRequest.getPlanId();
        SalesPlanSDO salesPlanSDO = salesPlanCommonService.checkSalesPlan(planId);
        // 所有部门
        List<Integer> statusList = Lists.newArrayList(SalesPlanStatusEnum.TO_BE_PUBLISHED.getCode(), SalesPlanStatusEnum.PUBLISHED.getCode());
        List<SalesPlanScItemExtendSDO> salesPlanScItemExtendSDOList = salesPlanCommonService.fetchPlanScItemExtendForDepartment(planId, statusList);
        log.info("SalesPlanPublishWriteServiceNewImpl_dispatchPlanScItemForCustomerNew_salesPlanScItemExtendSDOList_size={}",salesPlanScItemExtendSDOList.size());
        // 获取所有的计划明细
        PageQueryPlanLineRequest lineRequest = new PageQueryPlanLineRequest();
        lineRequest.setPlanId(planId);
        lineRequest.setStatus(SalesPlanLineStatusEnum.FINISH.getStatus());
        List<SalesPlanLineSDO> salesPlanLineList = queryAllSalesPlanDetail(lineRequest);
        log.info("SalesPlanPublishWriteServiceNewImpl_dispatchPlanScItemForCustomerNew_queryAllSalesPlanDetail_size={}",salesPlanLineList.size());
        if (CollectionUtils.isEmpty(salesPlanLineList)){
            throw new FacadeException("OTS-02-004-01-15-043");
        }
        Map</*部门Id + 货品Id */ String, List<SalesPlanLineSDO>> listMap = salesPlanLineList.stream().collect(Collectors.toMap(salesPlanLineSDO -> {
            return salesPlanLineSDO.getOrgSalesDepartmentCode() + salesPlanLineSDO.getScItemId();
        }, Lists::newArrayList, (a, b) -> {
            a.addAll(b);
            return a;
        }));
        Map</*计划明细ID*/ String, /*上旬回复量*/ BigDecimal> firstShareMap = Maps.newHashMap();
        Map</*计划明细ID*/ String, /*中旬回复量*/ BigDecimal> middleShareMap = Maps.newHashMap();
        Map</*计划明细ID*/ String, /*下旬回复量*/ BigDecimal> lastShareMap = Maps.newHashMap();
        for (SalesPlanScItemExtendSDO scItemExtendSDO : salesPlanScItemExtendSDOList) {
            // 部门+货品:上中下的回复量
//            Integer monthFirstCountByDepartment = scItemExtendSDO.getMonthFirstReturnCount();
//            Integer monthMiddleCountByDepartment = scItemExtendSDO.getMonthMiddleReturnCount();
//            Integer monthLastCountByDepartment = scItemExtendSDO.getMonthLastReturnCount();
            Integer monthFirstCountByDepartment = scItemExtendSDO.getMonthFirstTaskCount();
            Integer monthMiddleCountByDepartment = scItemExtendSDO.getMonthMiddleTaskCount();
            Integer monthLastCountByDepartment = scItemExtendSDO.getMonthLastTaskCount();
            if (monthFirstCountByDepartment == null && monthMiddleCountByDepartment == null && monthLastCountByDepartment == null){
                log.error("SalesPlanPublishWriteServiceNewImpl.dispatchPlanScItemForCustomerNew 没有部门回复量"+scItemExtendSDO);
                continue;
            }
            // 获取部门下该货品的计划明细
            List<SalesPlanLineSDO> salesPlanLineSDOS = listMap.get(scItemExtendSDO.getBizId() + scItemExtendSDO.getScItemId());
            if (CollectionUtils.isEmpty(salesPlanLineSDOS)){
                log.error("SalesPlanPublishWriteServiceNewImpl.dispatchPlanScItemForCustomerNew 没有部门计划明细"+scItemExtendSDO);
                continue;
            }
            log.info("SalesPlanPublishWriteServiceNewImpl_dispatchPlanScItemForCustomerNew_salesPlanScItemExtendSDOList_foreach_department={},get_salesPlanLineSDOS={}",JSONObject.toJSON(scItemExtendSDO),JSONObject.toJSON(salesPlanLineSDOS));
            // 部门回复量大于等于提报量 产品牧常说把这个校验去掉
//            if (scItemExtendSDO.getTaskCount() >= scItemExtendSDO.getMonthCount()){
//                salesPlanLineSDOS.forEach(salesPlanLineSDO -> {
//                    firstShareMap.put(salesPlanLineSDO.getId().toString(),new BigDecimal(salesPlanLineSDO.getMonthFirstCount()));
//                    middleShareMap.put(salesPlanLineSDO.getId().toString(),new BigDecimal(salesPlanLineSDO.getMonthMiddleCount()));
//                    lastShareMap.put(salesPlanLineSDO.getId().toString(),new BigDecimal(salesPlanLineSDO.getMonthLastCount()));
//                });
//                continue;
//            }
            Map</*客户地址*/ String, /*提报量*/ BigDecimal> firstCollect = Maps.newHashMap();
            Map<String, BigDecimal> middleCollect = Maps.newHashMap();
            Map<String, BigDecimal> lastCollect = Maps.newHashMap();
            // 获取每条明细的上中下的提报量
            for (SalesPlanLineSDO salesPlanLineSDO : salesPlanLineSDOS) {
                firstCollect.put(salesPlanLineSDO.getId().toString(),new BigDecimal(salesPlanLineSDO.getMonthFirstCount()));
                middleCollect.put(salesPlanLineSDO.getId().toString(),new BigDecimal(salesPlanLineSDO.getMonthMiddleCount()));
                lastCollect.put(salesPlanLineSDO.getId().toString(),new BigDecimal(salesPlanLineSDO.getMonthLastCount()));
            }

            //客户x货品 计划量都是0，平分部门任务量,,赋值1
            List<BigDecimal> firstCollectFilter = firstCollect.values().stream().filter(x->x.compareTo(BigDecimal.ZERO)==0).collect(Collectors.toList());
            if(firstCollectFilter.size()==firstCollect.size()){
                for (SalesPlanLineSDO salesPlanLineSDO : salesPlanLineSDOS) {
                    firstCollect.put(salesPlanLineSDO.getId().toString(),new BigDecimal(1));
                }
            }
            List<BigDecimal> middleCollectFilter = middleCollect.values().stream().filter(x->x.compareTo(BigDecimal.ZERO)==0).collect(Collectors.toList());
            if(middleCollectFilter.size()==middleCollect.size()){
                for (SalesPlanLineSDO salesPlanLineSDO : salesPlanLineSDOS) {
                    middleCollect.put(salesPlanLineSDO.getId().toString(),new BigDecimal(1));
                }
            }
            List<BigDecimal> lastCollectFilter = lastCollect.values().stream().filter(x->x.compareTo(BigDecimal.ZERO)==0).collect(Collectors.toList());
            if(lastCollectFilter.size()==lastCollect.size()){
                for (SalesPlanLineSDO salesPlanLineSDO : salesPlanLineSDOS) {
                    lastCollect.put(salesPlanLineSDO.getId().toString(),new BigDecimal(1));
                }
            }

            // 计划明细的上中下回复量
            SharePie firstSharePie = new SharePie(firstCollect);
            SharePie middleSharePie = new SharePie(middleCollect);
            SharePie lastSharePie = new SharePie(lastCollect);
            if (monthFirstCountByDepartment != null && monthFirstCountByDepartment >= 0){
                Map<String, BigDecimal> firstShare = firstSharePie.share(new BigDecimal(monthFirstCountByDepartment), 0, RoundingMode.DOWN);
                firstShareMap.putAll(firstShare);
            }
            if (monthMiddleCountByDepartment != null && monthMiddleCountByDepartment >= 0){
                Map<String, BigDecimal> middleShare = middleSharePie.share(new BigDecimal(monthMiddleCountByDepartment), 0, RoundingMode.DOWN);
                middleShareMap.putAll(middleShare);
            }
            if (monthLastCountByDepartment != null && monthLastCountByDepartment >= 0){
                Map<String, BigDecimal> lastShare = lastSharePie.share(new BigDecimal(monthLastCountByDepartment), 0, RoundingMode.DOWN);
                lastShareMap.putAll(lastShare);
            }
        }
        for (SalesPlanLineSDO salesPlanLineSDO : salesPlanLineList) {
            String lineId = salesPlanLineSDO.getId().toString();
            // 上旬回复值
            BigDecimal firstBigDecimal = firstShareMap.get(lineId);
            // 中心回复值
            BigDecimal middleBigDecimal = middleShareMap.get(lineId);
            // 下旬回复值
            BigDecimal lastBigDecimal = lastShareMap.get(lineId);

            if (firstBigDecimal == null && middleBigDecimal == null && lastBigDecimal == null){
                log.error("SalesPlanPublishWriteServiceNewImpl_dispatchPlanScItemForCustomerNew_salesPlanLineList_no_return："+salesPlanLineSDO);
                continue;
            }
            if (firstBigDecimal != null){
                salesPlanLineSDO.setMonthFirstReturnCount(firstBigDecimal.intValue());
                salesPlanLineSDO.setMonthFirstTaskCount(firstBigDecimal.intValue());
            }
            if (middleBigDecimal != null){
                salesPlanLineSDO.setMonthMiddleReturnCount(middleBigDecimal.intValue());
                salesPlanLineSDO.setMonthMiddleTaskCount(middleBigDecimal.intValue());
            }
            if (lastBigDecimal != null){
                salesPlanLineSDO.setMonthLastReturnCount(lastBigDecimal.intValue());
                salesPlanLineSDO.setMonthLastTaskCount(lastBigDecimal.intValue());
            }

            Integer monthReturnCountByCustomer = salesPlanLineSDO.getMonthFirstReturnCount() + salesPlanLineSDO.getMonthMiddleReturnCount() + salesPlanLineSDO.getMonthLastReturnCount();
            salesPlanLineSDO.setTaskCount(monthReturnCountByCustomer);
            //salesPlanLineSDO.setInitTaskCount(monthReturnCountByCustomer);
            int taskCount = salesPlanLineSDO.getTaskCount() != null ? salesPlanLineSDO.getTaskCount().intValue() : 0;
            long costInsuranceFreight = salesPlanLineSDO.getCostInsuranceFreight() != null ? salesPlanLineSDO.getCostInsuranceFreight().longValue() : 0l;
            salesPlanLineSDO.setBeforeTaskMoney(taskCount*costInsuranceFreight);
            //salesPlanLineSDO.setInitBeforeTaskMoney(salesPlanLineSDO.getBeforeTaskMoney());
            salesPlanLineSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
            //salesPlanLineSDO.setInitTaskMoney(salesPlanLineSDO.getTaskMoney());
            salesPlanLineSDO.calculateTaskSatisfaction();
            // 到岸折前金额
            long outboundPrice = Objects.nonNull(salesPlanLineSDO.getOutboundPrice()) ? salesPlanLineSDO.getOutboundPrice() : 0;
            long beforeMonthOutboundMoney = salesPlanLineSDO.getTaskCount() * outboundPrice;
            salesPlanLineSDO.setBeforeMonthOutboundMoney(beforeMonthOutboundMoney);
            // 到岸折后金额 到岸折后金额=到岸折前金额*(1-预估价格折扣率)
            salesPlanLineSDO.calculateMonthOutboundMoney(salesPlanSDO.getEstimateDiscount());
            // TODO 批量更新
            salesPlanLineRepository.edit(salesPlanLineSDO);
        }

        // 更新发布状态为已生成客户任务量
        SalesPlanSDO updateCheckStatusSalesPlanSDO = new SalesPlanSDO();
        updateCheckStatusSalesPlanSDO.setId(Long.valueOf(planId));
        updateCheckStatusSalesPlanSDO.setCheckStatus(SalesPlanCheckStatusEnum.DECOMPOSE_CUSTOMER_TASK.getCode());
        salesPlanRepository.editSalesPlan(updateCheckStatusSalesPlanSDO);

        //20220915 这里需要触发月度计划汇总，在发布客户任务量时触发  SalesPlanWriteServiceImpl.updateSalesPlanStatus
//        try {
//            // 生成客户任务量的时候，触发一下月度计划汇总
//            log.info("dispatchPlanScItemForCustomer_salesPlanCollectListener{}", planId);
//            Boolean aBoolean = messageService.sendMessage("sales_plan_publish_message", "CR_SALES_PLAN_V2", "salesPlan_collect", planId);
//        } catch (Exception e) {
//            log.error("月度计划生成客户任务量触发汇总的消息发送异常", e);
//        }
    }

    private List<SalesPlanLineSDO> queryAllSalesPlanDetail(PageQueryPlanLineRequest  request){
        int size = Math.toIntExact(BcOrderConstants.SALES_DETAIL_QUERY_SIZE() == null ? 500 : BcOrderConstants.SALES_DETAIL_QUERY_SIZE());
        request.setSize(size);
        request.setPage(1);
        PageResult<List<SalesPlanLineSDO>> listPageResult = salesPlanLineRepository.pageQuerySalesPlanLineList(request);
        if (listPageResult == null || CollectionUtils.isEmpty(listPageResult.getContent())){
            log.error("SalesPlanPublishWriteServiceNewImpl.queryAllSalesPlanDetail result:"+listPageResult);
            throw new FacadeException("OTS-02-004-01-15-043");
        }
        List<SalesPlanLineSDO> result = Lists.newArrayList(listPageResult.getContent());
        int total = listPageResult.getTotal();
        if (total <= size){
            return result;
        }
        int totalPage = (int) ((total + size - 1) / size);
        for (int i = 2; i <= totalPage; i++) {
            request.setPage(i);
            listPageResult = salesPlanLineRepository.pageQuerySalesPlanLineList(request);
            if (listPageResult == null || CollectionUtils.isEmpty(listPageResult.getContent())){
                return result;
            }
            result.addAll(listPageResult.getContent());
        }
        return result;
    }

    @Override
    public void dispatchPlanScItemForAdjustNew(DispatchPlanScItemRequest dispatchPlanScItemRequest) {
        //1、相关参数校验
        String adjustId = dispatchPlanScItemRequest.getAdjustId();
        if (StringUtils.isBlank(adjustId)) {
            throw new ActivityException("OTS-02-004-01-15-018");
        }
        SalesPlanSDO salesPlanSDO = salesPlanCommonService.checkSalesPlan(adjustId);
        Integer planId = salesPlanSDO.getParentId();
        // 2、查询业务单元的调整明细（回复过来）
        QueryPlanScItemExtendRequest queryPlanScItemExtendRequest = new QueryPlanScItemExtendRequest();
        // 计划id
        queryPlanScItemExtendRequest.setPlanId(Long.valueOf(planId));
        // 业务类型是调整
        queryPlanScItemExtendRequest.setModel(SalesPlanModelEnum.ADJUST_MODEL.getModel());
        // 查询维度是业务单元
        queryPlanScItemExtendRequest.setBizType(SalesStatBizTypeEnum.BUSINESS_UNIT.getBizType());
        // 查询具体的业务单元
        queryPlanScItemExtendRequest.setBizId(salesPlanSDO.getOrgBusinessUnitCode());
        // 货品维度
        queryPlanScItemExtendRequest.setPoolType(Objects.toString(StatPoolTypeEnum.SC_ITEM.getPoolType()));
        log.info("查询业务单元调整回复量参数：{}", JSON.toJSONString(queryPlanScItemExtendRequest));
        List<SalesPlanScItemExtendSDO> salesPlanScItemExtendSDOList = salesPlanScItemExtendRepository.batchQueryPlanScItemExtend(queryPlanScItemExtendRequest);
        log.info("查询业务单元调整回复量结果：{}", JSON.toJSONString(queryPlanScItemExtendRequest));
        if (CollectionUtils.isEmpty(salesPlanScItemExtendSDOList)) {
            throw new FacadeException("OTS-02-004-01-16-017");
        }
        // 遍历业务单元的调整明细，则按最小货品维度
        for (SalesPlanScItemExtendSDO salesPlanScItemExtendSDO : salesPlanScItemExtendSDOList) {
            // 业务单元调整量
            Integer adjustCount = salesPlanScItemExtendSDO.getAdjustCount();
            // 业务单元上旬调整回复量
            Integer monthFirstAdjustReturnCount = salesPlanScItemExtendSDO.getMonthFirstAdjustReturnCount();
            // 业务单元中旬调整回复量
            Integer monthMiddleAdjustReturnCount = salesPlanScItemExtendSDO.getMonthMiddleAdjustReturnCount();
            // 业务单元下旬调整回复量
            Integer monthLastAdjustReturnCount = salesPlanScItemExtendSDO.getMonthLastAdjustReturnCount();
            // 业务单元计划调整回复量总和
            Integer monthAdjustReturnCount = monthFirstAdjustReturnCount + monthMiddleAdjustReturnCount + monthLastAdjustReturnCount;

            // 业务单元下的每个货品
            // 查询客户货品的调整明细
            String scItemId = salesPlanScItemExtendSDO.getScItemId();
            SalesPlanAdjustDetailBatchQueryRequest salesPlanAdjustDetailBatchQueryRequest = new SalesPlanAdjustDetailBatchQueryRequest();
            salesPlanAdjustDetailBatchQueryRequest.setAdjustId(adjustId);
            salesPlanAdjustDetailBatchQueryRequest.setScItemId(scItemId);
            salesPlanAdjustDetailBatchQueryRequest.setOrgBusinessUnitCode(salesPlanSDO.getOrgBusinessUnitCode());
            List<SalesPlanAdjustDetailSDO> salesPlanAdjustDetailSDOList = salesPlanAdjustDetailRepository.batchQueryDetail(salesPlanAdjustDetailBatchQueryRequest);

            // 比较业务单元货品调整回复量和货品调整提报量的大小
            // 调整回复量大于调整提报量，按调整提报量向下回复（原调整三旬调整提报量就是调整回复量）
            if (monthAdjustReturnCount >= adjustCount) {
                salesPlanAdjustDetailSDOList.stream().forEach(salesPlanAdjustDetailSDO -> {
                    SalesPlanAdjustDetailSDO editSalesPlanAdjustDetailSDO = SalesPlanAdjustDetailSDO.builder().build();
                    BeanUtils.copyProperties(salesPlanAdjustDetailSDO, editSalesPlanAdjustDetailSDO);
                    // 拿到上旬调整量（准确说叫调整提报量）
                    Integer monthFirstAdjustCount = salesPlanAdjustDetailSDO.getMonthFirstAdjustCount();
                    // 赋值给上旬调整回复量和调整总量
                    editSalesPlanAdjustDetailSDO.setMonthFirstAdjustReturnCount(monthFirstAdjustCount);
                    editSalesPlanAdjustDetailSDO.setMonthFirstAdjustTotalCount(monthFirstAdjustCount);

                    // 拿到中旬调整量（准确说叫调整提报量）
                    Integer monthMiddleAdjustCount = salesPlanAdjustDetailSDO.getMonthMiddleAdjustCount();
                    // 赋值给中旬调整回复量和调整总量
                    editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustReturnCount(monthMiddleAdjustCount);
                    editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustTotalCount(monthMiddleAdjustCount);

                    // 拿到下旬调整量（准确说叫调整提报量）
                    Integer monthLastAdjustCount = salesPlanAdjustDetailSDO.getMonthLastAdjustCount();
                    // 赋值给下旬调整回复量和调整总量
                    editSalesPlanAdjustDetailSDO.setMonthLastAdjustReturnCount(monthLastAdjustCount);
                    editSalesPlanAdjustDetailSDO.setMonthLastAdjustTotalCount(monthLastAdjustCount);

                    // 计算折前任务金额
//                    editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(editSalesPlanAdjustDetailSDO.getTaskCount() * editSalesPlanAdjustDetailSDO.getCostInsuranceFreight());
//                    // 计算折后任务金额
//                    editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
//                    editSalesPlanAdjustDetailSDO.calculateTaskSatisfaction();

                    //到岸折前金额
                    Integer monthAdjustReturnCountOutbound = editSalesPlanAdjustDetailSDO.getMonthFirstAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthMiddleAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthLastAdjustReturnCount();
                    long outboundPrice = Objects.nonNull(editSalesPlanAdjustDetailSDO.getOutboundPrice()) ? editSalesPlanAdjustDetailSDO.getOutboundPrice() : 0;
                    long beforeMonthOutboundMoney = (editSalesPlanAdjustDetailSDO.getTaskCount() + monthAdjustReturnCountOutbound) * outboundPrice;
                    editSalesPlanAdjustDetailSDO.setBeforeMonthOutboundMoney(beforeMonthOutboundMoney);
                    //到岸折后金额 到岸折后金额=到岸折前金额*(1-预估价格折扣率)
                    editSalesPlanAdjustDetailSDO.calculateMonthOutboundMoney(salesPlanSDO.getEstimateDiscount());
                    // 更新调整明细表
                    //折前满足度=（任务量+调整提报量）*货品常规价
                    Integer taskAdjustCount = editSalesPlanAdjustDetailSDO.getTaskCount() + editSalesPlanAdjustDetailSDO.getAdjustCount();
                    Long CostInsuranceFreight = editSalesPlanAdjustDetailSDO.getCostInsuranceFreight();
                    //不落库的折前任务金额
                    long notBeforeContractSatisfactionMoney = taskAdjustCount * CostInsuranceFreight;
                    //落库的折前任务金额
                    long beforeContractSatisfactionMoney = editSalesPlanAdjustDetailSDO.getTaskCount() * CostInsuranceFreight;
                    editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(beforeContractSatisfactionMoney);
                    BigDecimal beforeContractSatisfaction = CalculatePercentage.retainTwo(notBeforeContractSatisfactionMoney, editSalesPlanAdjustDetailSDO.getBeforeMonthContractMoney());
                    editSalesPlanAdjustDetailSDO.setBeforeContractSatisfaction(beforeContractSatisfaction);
                    //折后满足度=（任务量+调整提报量）*货品常规价*（1-预估价格折扣率）
                    BigDecimal multiply = salesPlanSDO.getEstimateDiscount().multiply(new BigDecimal("0.01").setScale(2, BigDecimal.ROUND_HALF_UP));
                    BigDecimal subtract = new BigDecimal(1).subtract(multiply);
                    BigDecimal ContractSatisfactionMoney = new BigDecimal(taskAdjustCount).multiply(new BigDecimal(CostInsuranceFreight)).multiply(subtract);
                    //折后任务金额
                    editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                    BigDecimal contractSatisfaction = CalculatePercentage.retainTwoBigDecimal(ContractSatisfactionMoney, new BigDecimal(editSalesPlanAdjustDetailSDO.getMonthContractMoney()));
                    editSalesPlanAdjustDetailSDO.setContractSatisfaction(contractSatisfaction);
                    salesPlanAdjustDetailRepository.edit(editSalesPlanAdjustDetailSDO);
                });
            }
            // 比较业务单元货品调整回复量和货品调整提报量的大小
            // 调整回复量小于调整提报量，按调整回复量根据计算规则向下回复
            else {
                // 2、将调整量正负值的分别筛选出来 (上中下询)
                // 上旬调整量为负的列表
                List<SalesPlanAdjustDetailSDO> firstAdjustNegativeDetail = salesPlanAdjustDetailSDOList.stream()
                        .filter(salesPlanAdjustDetailSDO -> salesPlanAdjustDetailSDO.getMonthFirstAdjustCount() < 0).collect(Collectors.toList());
                // 上旬调整量为正的列表
                List<SalesPlanAdjustDetailSDO> firstAdjustPositiveDetail = salesPlanAdjustDetailSDOList.stream()
                        .filter(salesPlanAdjustDetailSDO -> salesPlanAdjustDetailSDO.getMonthFirstAdjustCount() >= 0).collect(Collectors.toList());

                // 中旬调整量为负的列表
                List<SalesPlanAdjustDetailSDO> middleAdjustNegativeDetail = salesPlanAdjustDetailSDOList.stream()
                        .filter(salesPlanAdjustDetailSDO -> salesPlanAdjustDetailSDO.getMonthMiddleAdjustCount() < 0).collect(Collectors.toList());
                // 中旬调整量为正的列表
                List<SalesPlanAdjustDetailSDO> middleAdjustPositiveDetail = salesPlanAdjustDetailSDOList.stream()
                        .filter(salesPlanAdjustDetailSDO -> salesPlanAdjustDetailSDO.getMonthMiddleAdjustCount() >= 0).collect(Collectors.toList());

                // 下旬调整量为负的列表
                List<SalesPlanAdjustDetailSDO> lastAdjustNegativeDetail = salesPlanAdjustDetailSDOList.stream()
                        .filter(salesPlanAdjustDetailSDO -> salesPlanAdjustDetailSDO.getMonthLastAdjustCount() < 0).collect(Collectors.toList());
                // 下旬调整量为正的列表
                List<SalesPlanAdjustDetailSDO> lastAdjustPositiveDetail = salesPlanAdjustDetailSDOList.stream()
                        .filter(salesPlanAdjustDetailSDO -> salesPlanAdjustDetailSDO.getMonthLastAdjustCount() >= 0).collect(Collectors.toList());

                // 客户上旬调整量负值的和
                Integer firstAdjustNegativeSum = firstAdjustNegativeDetail.stream().mapToInt(SalesPlanAdjustDetailSDO::getMonthFirstAdjustCount).sum();
                // 客户上旬调整量正值的和
                Integer firstAdjustPositiveSum = firstAdjustPositiveDetail.stream().mapToInt(SalesPlanAdjustDetailSDO::getMonthFirstAdjustCount).sum();

                // 客户中旬调整量负值的和
                Integer middleAdjustNegativeSum = middleAdjustNegativeDetail.stream().mapToInt(SalesPlanAdjustDetailSDO::getMonthMiddleAdjustCount).sum();
                // 客户中旬调整量正值的和
                Integer middleAdjustPositiveSum = middleAdjustPositiveDetail.stream().mapToInt(SalesPlanAdjustDetailSDO::getMonthMiddleAdjustCount).sum();

                // 客户下旬调整量负值的和
                Integer lastAdjustNegativeSum = lastAdjustNegativeDetail.stream().mapToInt(SalesPlanAdjustDetailSDO::getMonthLastAdjustCount).sum();
                // 客户下旬调整量正值的和
                Integer lastAdjustPositiveSum = lastAdjustPositiveDetail.stream().mapToInt(SalesPlanAdjustDetailSDO::getMonthLastAdjustCount).sum();

                // 业务单元货品计划调整回复量=0的情况，此时，客户调多少就返回多少
                if (monthAdjustReturnCount == 0) {
                    for (SalesPlanAdjustDetailSDO salesPlanAdjustDetailSDO : salesPlanAdjustDetailSDOList) {
                        SalesPlanAdjustDetailSDO editSalesPlanAdjustDetailSDO = SalesPlanAdjustDetailSDO.builder().build();
                        BeanUtils.copyProperties(salesPlanAdjustDetailSDO, editSalesPlanAdjustDetailSDO);
                        // 拿到上旬调整量（准确说叫调整提报量）
                        Integer monthFirstAdjustCount = salesPlanAdjustDetailSDO.getMonthFirstAdjustCount();
                        // 赋值给上旬调整回复量和调整总量
                        editSalesPlanAdjustDetailSDO.setMonthFirstAdjustReturnCount(monthFirstAdjustCount);
                        editSalesPlanAdjustDetailSDO.setMonthFirstAdjustTotalCount(monthFirstAdjustCount);

                        // 拿到中旬调整量（准确说叫调整提报量）
                        Integer monthMiddleAdjustCount = salesPlanAdjustDetailSDO.getMonthMiddleAdjustCount();
                        // 赋值给上旬调整回复量和调整总量
                        editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustReturnCount(monthMiddleAdjustCount);
                        editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustTotalCount(monthMiddleAdjustCount);

                        // 拿到中旬调整量（准确说叫调整提报量）
                        Integer monthLastAdjustCount = salesPlanAdjustDetailSDO.getMonthLastAdjustCount();
                        // 赋值给上旬调整回复量和调整总量
                        editSalesPlanAdjustDetailSDO.setMonthLastAdjustReturnCount(monthLastAdjustCount);
                        editSalesPlanAdjustDetailSDO.setMonthLastAdjustTotalCount(monthLastAdjustCount);

                     /*   // 计算折前任务金额
                        editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(editSalesPlanAdjustDetailSDO.getTaskCount() * editSalesPlanAdjustDetailSDO.getCostInsuranceFreight());
                        // 计算折后任务金额
                        editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                        editSalesPlanAdjustDetailSDO.calculateTaskSatisfaction();*/

                        //到岸折前金额
                        Integer monthAdjustReturnCountOutbound = editSalesPlanAdjustDetailSDO.getMonthFirstAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthMiddleAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthLastAdjustReturnCount();
                        long outboundPrice = Objects.nonNull(editSalesPlanAdjustDetailSDO.getOutboundPrice()) ? editSalesPlanAdjustDetailSDO.getOutboundPrice() : 0;
                        long beforeMonthOutboundMoney = (editSalesPlanAdjustDetailSDO.getTaskCount() + monthAdjustReturnCountOutbound) * outboundPrice;
                        editSalesPlanAdjustDetailSDO.setBeforeMonthOutboundMoney(beforeMonthOutboundMoney);
                        //到岸折后金额 到岸折后金额=到岸折前金额*(1-预估价格折扣率)
                        editSalesPlanAdjustDetailSDO.calculateMonthOutboundMoney(salesPlanSDO.getEstimateDiscount());
                        //折前满足度=（任务量+调整提报量）*货品常规价
                        Integer taskAdjustCount = editSalesPlanAdjustDetailSDO.getTaskCount() + editSalesPlanAdjustDetailSDO.getAdjustCount();
                        Long CostInsuranceFreight = editSalesPlanAdjustDetailSDO.getCostInsuranceFreight();
                        //不落库的折前任务金额
                        long notBeforeContractSatisfactionMoney = taskAdjustCount * CostInsuranceFreight;
                        //落库的折前任务金额
                        long beforeContractSatisfactionMoney = editSalesPlanAdjustDetailSDO.getTaskCount() * CostInsuranceFreight;
                        //折前任务金额
                        editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(beforeContractSatisfactionMoney);
                        BigDecimal beforeContractSatisfaction = CalculatePercentage.retainTwo(notBeforeContractSatisfactionMoney, editSalesPlanAdjustDetailSDO.getBeforeMonthContractMoney());
                        editSalesPlanAdjustDetailSDO.setBeforeContractSatisfaction(beforeContractSatisfaction);
                        //折后满足度=（任务量+调整提报量）*货品常规价*（1-预估价格折扣率）
                        BigDecimal multiply = salesPlanSDO.getEstimateDiscount().multiply(new BigDecimal("0.01").setScale(2, BigDecimal.ROUND_HALF_UP));
                        BigDecimal subtract = new BigDecimal(1).subtract(multiply);
                        BigDecimal ContractSatisfactionMoney = new BigDecimal(taskAdjustCount).multiply(new BigDecimal(CostInsuranceFreight)).multiply(subtract);
                        //折后任务金额
                        editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                        BigDecimal contractSatisfaction = CalculatePercentage.retainTwoBigDecimal(ContractSatisfactionMoney, new BigDecimal(editSalesPlanAdjustDetailSDO.getMonthContractMoney()));
                        editSalesPlanAdjustDetailSDO.setContractSatisfaction(contractSatisfaction);
                        // 更新调整明细表
                        salesPlanAdjustDetailRepository.edit(editSalesPlanAdjustDetailSDO);
                    }
                }
                // 业务单元货品计划调整回复量>0的情况
                else if (monthAdjustReturnCount > 0) {
                    // 业务单元回复值   业务单元货品计划调整回复量-（客户调整量负值）
                    // 业务单元上旬实际回复值
                    Integer monthFirstAdjustTheoryReturnCount = monthFirstAdjustReturnCount - firstAdjustNegativeSum;
                    // 业务单元中旬实际回复值
                    Integer monthMiddleAdjustTheoryReturnCount = monthMiddleAdjustReturnCount - middleAdjustNegativeSum;
                    // 业务单元下旬实际回复值
                    Integer monthLastAdjustTheoryReturnCount = monthLastAdjustReturnCount - lastAdjustNegativeSum;
                    // 遍历客户调整明细
                    for (SalesPlanAdjustDetailSDO salesPlanAdjustDetailSDO : salesPlanAdjustDetailSDOList) {

                        SalesPlanAdjustDetailSDO editSalesPlanAdjustDetailSDO = SalesPlanAdjustDetailSDO.builder().build();
                        BeanUtils.copyProperties(salesPlanAdjustDetailSDO, editSalesPlanAdjustDetailSDO);
                        // 上旬调整提报量
                        Integer monthFirstAdjustCount = salesPlanAdjustDetailSDO.getMonthFirstAdjustCount();
                        // 计算上旬调整回复量
                        Integer firstAdjustReturnCount = getMonthAdjustReturnCountDefaultNegative(monthFirstAdjustCount, firstAdjustPositiveSum, monthFirstAdjustTheoryReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthFirstAdjustReturnCount(firstAdjustReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthFirstAdjustTotalCount(firstAdjustReturnCount);

                        // 中旬调整提报量
                        Integer monthMiddleAdjustCount = salesPlanAdjustDetailSDO.getMonthMiddleAdjustCount();
                        // 计算中旬调整回复量
                        Integer middleAdjustReturnCount = getMonthAdjustReturnCountDefaultNegative(monthMiddleAdjustCount, middleAdjustPositiveSum, monthMiddleAdjustTheoryReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustReturnCount(middleAdjustReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustTotalCount(middleAdjustReturnCount);

                        // 下旬调整提报量
                        Integer monthLastAdjustCount = salesPlanAdjustDetailSDO.getMonthLastAdjustCount();
                        // 计算下旬调整回复量
                        Integer lastAdjustReturnCount = getMonthAdjustReturnCountDefaultNegative(monthLastAdjustCount, lastAdjustPositiveSum, monthLastAdjustTheoryReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthLastAdjustReturnCount(lastAdjustReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthLastAdjustTotalCount(lastAdjustReturnCount);

                        /*// 计算折前任务金额
                        editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(editSalesPlanAdjustDetailSDO.getTaskCount() * editSalesPlanAdjustDetailSDO.getCostInsuranceFreight());
                        // 计算折后任务金额
                        editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                        editSalesPlanAdjustDetailSDO.calculateTaskSatisfaction();*/
                        //到岸折前金额
                        Integer monthAdjustReturnCountOutbound = editSalesPlanAdjustDetailSDO.getMonthFirstAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthMiddleAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthLastAdjustReturnCount();
                        long outboundPrice = Objects.nonNull(editSalesPlanAdjustDetailSDO.getOutboundPrice()) ? editSalesPlanAdjustDetailSDO.getOutboundPrice() : 0;
                        long beforeMonthOutboundMoney = (editSalesPlanAdjustDetailSDO.getTaskCount() + monthAdjustReturnCountOutbound) * outboundPrice;
                        editSalesPlanAdjustDetailSDO.setBeforeMonthOutboundMoney(beforeMonthOutboundMoney);
                        //到岸折后金额 到岸折后金额=到岸折前金额*(1-预估价格折扣率)
                        editSalesPlanAdjustDetailSDO.calculateMonthOutboundMoney(salesPlanSDO.getEstimateDiscount());
                        //折前满足度=（任务量+调整提报量）*货品常规价
                        Integer taskAdjustCount = editSalesPlanAdjustDetailSDO.getTaskCount() + editSalesPlanAdjustDetailSDO.getAdjustCount();
                        Long CostInsuranceFreight = editSalesPlanAdjustDetailSDO.getCostInsuranceFreight();
                        //不落库的折前任务金额
                        long notBeforeContractSatisfactionMoney = taskAdjustCount * CostInsuranceFreight;
                        //落库的折前任务金额
                        long beforeContractSatisfactionMoney = editSalesPlanAdjustDetailSDO.getTaskCount() * CostInsuranceFreight;
                        //折前任务金额
                        editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(beforeContractSatisfactionMoney);
                        BigDecimal beforeContractSatisfaction = CalculatePercentage.retainTwo(notBeforeContractSatisfactionMoney, editSalesPlanAdjustDetailSDO.getBeforeMonthContractMoney());
                        editSalesPlanAdjustDetailSDO.setBeforeContractSatisfaction(beforeContractSatisfaction);
                        //折后满足度=（任务量+调整提报量）*货品常规价*（1-预估价格折扣率）
                        BigDecimal multiply = salesPlanSDO.getEstimateDiscount().multiply(new BigDecimal("0.01").setScale(2, BigDecimal.ROUND_HALF_UP));
                        BigDecimal subtract = new BigDecimal(1).subtract(multiply);
                        BigDecimal ContractSatisfactionMoney = new BigDecimal(taskAdjustCount).multiply(new BigDecimal(CostInsuranceFreight)).multiply(subtract);
                        //折后任务金额
                        editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                        BigDecimal contractSatisfaction = CalculatePercentage.retainTwoBigDecimal(ContractSatisfactionMoney, new BigDecimal(editSalesPlanAdjustDetailSDO.getMonthContractMoney()));
                        editSalesPlanAdjustDetailSDO.setContractSatisfaction(contractSatisfaction);
                        // 更新调整明细表
                        salesPlanAdjustDetailRepository.edit(editSalesPlanAdjustDetailSDO);
                    }
                }
                // 业务单元货品计划调整回复量<0的情况
                else if (monthAdjustReturnCount < 0) {
                    // 业务单元回复值   业务单元货品计划调整回复量-（客户调整量正值）
                    // 业务单元上旬实际回复值
                    Integer monthFirstAdjustTheoryReturnCount = monthFirstAdjustReturnCount - firstAdjustPositiveSum;
                    // 业务单元中旬实际回复值
                    Integer monthMiddleAdjustTheoryReturnCount = monthMiddleAdjustReturnCount - middleAdjustPositiveSum;
                    // 业务单元下旬实际回复值
                    Integer monthLastAdjustTheoryReturnCount = monthLastAdjustReturnCount - lastAdjustPositiveSum;
                    // 遍历客户调整明细
                    for (SalesPlanAdjustDetailSDO salesPlanAdjustDetailSDO : salesPlanAdjustDetailSDOList) {
                        SalesPlanAdjustDetailSDO editSalesPlanAdjustDetailSDO = SalesPlanAdjustDetailSDO.builder().build();
                        BeanUtils.copyProperties(salesPlanAdjustDetailSDO, editSalesPlanAdjustDetailSDO);
                        // 上旬调整提报量
                        Integer monthFirstAdjustCount = salesPlanAdjustDetailSDO.getMonthFirstAdjustCount();
                        // 计算上旬调整回复量
                        Integer firstAdjustReturnCount = getMonthAdjustReturnCountDefaultPositive(monthFirstAdjustCount, firstAdjustNegativeSum, monthFirstAdjustTheoryReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthFirstAdjustReturnCount(firstAdjustReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthFirstAdjustTotalCount(firstAdjustReturnCount);

                        // 中旬调整提报量
                        Integer monthMiddleAdjustCount = salesPlanAdjustDetailSDO.getMonthMiddleAdjustCount();
                        // 计算中旬调整回复量
                        Integer middleAdjustReturnCount = getMonthAdjustReturnCountDefaultPositive(monthMiddleAdjustCount, middleAdjustNegativeSum, monthMiddleAdjustTheoryReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustReturnCount(middleAdjustReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthMiddleAdjustTotalCount(middleAdjustReturnCount);

                        // 下旬调整提报量
                        Integer monthLastAdjustCount = salesPlanAdjustDetailSDO.getMonthLastAdjustCount();
                        // 计算下旬调整回复量
                        Integer lastAdjustReturnCount = getMonthAdjustReturnCountDefaultPositive(monthLastAdjustCount, lastAdjustNegativeSum, monthLastAdjustTheoryReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthLastAdjustReturnCount(lastAdjustReturnCount);
                        editSalesPlanAdjustDetailSDO.setMonthLastAdjustTotalCount(lastAdjustReturnCount);

                        /*// 计算折前任务金额
                        editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(editSalesPlanAdjustDetailSDO.getTaskCount() * editSalesPlanAdjustDetailSDO.getCostInsuranceFreight());
                        // 计算折后任务金额
                        editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                        editSalesPlanAdjustDetailSDO.calculateTaskSatisfaction();*/
                        //到岸折前金额
                        Integer monthAdjustReturnCountOutbound = editSalesPlanAdjustDetailSDO.getMonthFirstAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthMiddleAdjustReturnCount() + editSalesPlanAdjustDetailSDO.getMonthLastAdjustReturnCount();
                        long outboundPrice = Objects.nonNull(editSalesPlanAdjustDetailSDO.getOutboundPrice()) ? editSalesPlanAdjustDetailSDO.getOutboundPrice() : 0;
                        long beforeMonthOutboundMoney = (editSalesPlanAdjustDetailSDO.getTaskCount() + monthAdjustReturnCountOutbound) * outboundPrice;
                        editSalesPlanAdjustDetailSDO.setBeforeMonthOutboundMoney(beforeMonthOutboundMoney);
                        //到岸折后金额 到岸折后金额=到岸折前金额*(1-预估价格折扣率)
                        editSalesPlanAdjustDetailSDO.calculateMonthOutboundMoney(salesPlanSDO.getEstimateDiscount());
                        //折前满足度=（任务量+调整提报量）*货品常规价
                        Integer taskAdjustCount = editSalesPlanAdjustDetailSDO.getTaskCount() + editSalesPlanAdjustDetailSDO.getAdjustCount();
                        Long CostInsuranceFreight = editSalesPlanAdjustDetailSDO.getCostInsuranceFreight();
                        //不落库的折前任务金额
                        long notBeforeContractSatisfactionMoney = taskAdjustCount * CostInsuranceFreight;
                        //落库的折前任务金额
                        long beforeContractSatisfactionMoney = editSalesPlanAdjustDetailSDO.getTaskCount() * CostInsuranceFreight;
                        //折前任务金额
                        editSalesPlanAdjustDetailSDO.setBeforeTaskMoney(beforeContractSatisfactionMoney);
                        BigDecimal beforeContractSatisfaction = CalculatePercentage.retainTwo(notBeforeContractSatisfactionMoney, editSalesPlanAdjustDetailSDO.getBeforeMonthContractMoney());
                        editSalesPlanAdjustDetailSDO.setBeforeContractSatisfaction(beforeContractSatisfaction);
                        //折后满足度=（任务量+调整提报量）*货品常规价*（1-预估价格折扣率）
                        BigDecimal multiply = salesPlanSDO.getEstimateDiscount().multiply(new BigDecimal("0.01").setScale(2, BigDecimal.ROUND_HALF_UP));
                        BigDecimal subtract = new BigDecimal(1).subtract(multiply);
                        BigDecimal ContractSatisfactionMoney = new BigDecimal(taskAdjustCount).multiply(new BigDecimal(CostInsuranceFreight)).multiply(subtract);
                        //折后任务金额
                        editSalesPlanAdjustDetailSDO.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                        BigDecimal contractSatisfaction = CalculatePercentage.retainTwoBigDecimal(ContractSatisfactionMoney, new BigDecimal(editSalesPlanAdjustDetailSDO.getMonthContractMoney()));
                        editSalesPlanAdjustDetailSDO.setContractSatisfaction(contractSatisfaction);
                        // 更新调整明细表
                        salesPlanAdjustDetailRepository.edit(editSalesPlanAdjustDetailSDO);
                    }
                }
            }
        }
        // 查询调整id
        // 生成客户调整回复量时，修改checkStatus的状态
        SalesPlanSDO updateCheckStatusSalesPlanSDO = new SalesPlanSDO();
        updateCheckStatusSalesPlanSDO.setCheckStatus(SalesPlanAdjustCheckStatusEnum.DECOMPOSE_ADJUST_RETURN.getCode());
        updateCheckStatusSalesPlanSDO.setId(Long.valueOf(adjustId));
        updateCheckStatusSalesPlanSDO.setGmtModified(new Date());
        salesPlanRepository.editSalesPlan(updateCheckStatusSalesPlanSDO);
    }

    /**
     * 生成部门任务量
     *
     * @param dispatchPlanScItemRequest
     * @return
     */
    @Override
    public Boolean dispatchPlanScItemForDepartmentNew(DispatchPlanScItemRequest dispatchPlanScItemRequest) {
        // 0、基本参数校验
        checkParam(dispatchPlanScItemRequest);
        // 0.5 先删除在新增
        BatchDeletePlanScItemExtendRequest batchDeletePlanScItemExtendRequest = new BatchDeletePlanScItemExtendRequest();
        batchDeletePlanScItemExtendRequest.setPlanId(dispatchPlanScItemRequest.getPlanId());
        batchDeletePlanScItemExtendRequest.setBizType(SalesStatBizTypeEnum.SALES_DEPARTMENT.getBizType());
        batchDeletePlanScItemExtendRequest.setPoolType(StatPoolTypeEnum.SC_ITEM.getPoolType());
        log.info("dispatchPlanScItemForDepartmentNew_batchDeletePlanScItemExtend_request={}", JSONObject.toJSONString(batchDeletePlanScItemExtendRequest));
        salesPlanService.batchDeletePlanScItemExtend(batchDeletePlanScItemExtendRequest);
        // 1、根据计划id查询  计划是否是待发布状态
        String planId = dispatchPlanScItemRequest.getPlanId();
        SalesPlanSDO salesPlanSDO = checkPlan(planId);
        // 2、查询分子公司的货品回复量
        QueryPlanScItemExtendRequest queryPlanScItemExtendRequest = new QueryPlanScItemExtendRequest();
        queryPlanScItemExtendRequest.setPlanId(Long.valueOf(planId));
        queryPlanScItemExtendRequest.setBizType(SalesStatBizTypeEnum.BUSINESS_UNIT.getBizType());
        queryPlanScItemExtendRequest.setBizId(salesPlanSDO.getOrgBusinessUnitCode());
        queryPlanScItemExtendRequest.setPoolType(Objects.toString(StatPoolTypeEnum.SC_ITEM.getPoolType()));
        queryPlanScItemExtendRequest.setModel(SalesPlanModelEnum.PLAN_MODEL.getModel());
        log.info("dispatchPlanScItemForDepartmentNew_batchQueryPlanScItemExtend_request={}", JSON.toJSONString(queryPlanScItemExtendRequest));
        // 默认纪元分页已配置1000，产品确认公司货品量级100左右，1000是满足需求的
        List<SalesPlanScItemExtendSDO> salesPlanScItemExtendSDOList = salesPlanScItemExtendRepository.batchQueryPlanScItemExtend(queryPlanScItemExtendRequest);
        log.info("dispatchPlanScItemForDepartmentNew_batchQueryPlanScItemExtend_result={}", JSON.toJSONString(salesPlanScItemExtendSDOList));
        if (CollectionUtils.isEmpty(salesPlanScItemExtendSDOList)) {
            throw new FacadeException("OTS-02-004-01-15-009");
        }

        // 将分子公司下销售组织计划货品，部门计划货品数据统一做封装
        // 获取分子公司的策略
        SalesPlanStrategyDetailResponse salesPlanStrategyDetailResponse = getSalesPlanStrategyDetail(salesPlanSDO);
        // key:部门id；value：策略
        Map</* depId */ String, SalesPlanStrategyDetailSDO> salesPlanStrategyDetailSDOMap = salesPlanStrategyDetailResponse
                .getSalesPlanStrategyDetailDepartmentList()
                .stream().collect(Collectors.toMap(SalesPlanStrategyDetailSDO::getReplyTargetId, x -> x, (a, b) -> a));

        // 组织策略
        List<SalesOrgRatioDTO> salesOrgRatioDTOList = salesPlanStrategyDetailResponse.getSalesOrgRatioDTOList();
        CompanyScItemPlanInfo companyScItemPlanInfo = getCompanyScItemPlanInfo(planId, salesOrgRatioDTOList, salesPlanStrategyDetailResponse);

        List<SalesPlanScItemExtendSDO> planScItemExtendSDOAllList = new ArrayList<>();
        // 遍历分子公司的计划货品
        for (SalesPlanScItemExtendSDO salesPlanScItemExtendSDO : salesPlanScItemExtendSDOList) {
            // 业务单元货品计划提报量
            Integer monthCount = salesPlanScItemExtendSDO.getMonthCount();
            Integer monthFirstCount = salesPlanScItemExtendSDO.getMonthFirstCount();
            Integer monthMiddleCount = salesPlanScItemExtendSDO.getMonthMiddleCount();
            Integer monthLastCount = salesPlanScItemExtendSDO.getMonthLastCount();

            // 业务单元货品上旬回复量
            Integer monthFirstReturnCount = salesPlanScItemExtendSDO.getMonthFirstReturnCount();
            // 业务单元货品中旬回复量
            Integer monthMiddleReturnCount = salesPlanScItemExtendSDO.getMonthMiddleReturnCount();
            // 业务单元货品下旬回复量
            Integer monthLastReturnCount = salesPlanScItemExtendSDO.getMonthLastReturnCount();
            // 业务单元货品回复量总和
            Integer monthReturnCount = monthFirstReturnCount + monthMiddleReturnCount + monthLastReturnCount;

            //月度计划超量回复优化,  之前按回复量分解,后续统一按任务量生成部门任务量
            // (这里的任务量是新增的四个任务量字段,跟计划货品扩展任务量字段不是同一个,仅在计划发布页未生成部门任务量时显示)
            // 业务单元货品上旬任务量
            Integer monthFirstCompanyTaskCount = salesPlanScItemExtendSDO.getMonthFirstCompanyTaskCount() != null ? salesPlanScItemExtendSDO.getMonthFirstCompanyTaskCount() : 0;
            // 业务单元货品中旬任务量
            Integer monthMiddleCompanyTaskCount = salesPlanScItemExtendSDO.getMonthMiddleCompanyTaskCount() != null ? salesPlanScItemExtendSDO.getMonthMiddleCompanyTaskCount() : 0;
            // 业务单元货品下旬任务量
            Integer monthLastCompanyTaskCount = salesPlanScItemExtendSDO.getMonthLastCompanyTaskCount() != null ? salesPlanScItemExtendSDO.getMonthLastCompanyTaskCount() : 0;
            // 业务单元货品任务总和
            Integer monthCompanyTaskCount = monthFirstCompanyTaskCount + monthMiddleCompanyTaskCount + monthLastCompanyTaskCount;

            log.info("dispatchPlanScItemForDepartmentNew_foreach_salesPlanScItemExtendSDOList_SDO={}", JSON.toJSONString(salesPlanScItemExtendSDO));

            // 比较业务单元货品回复量和货品计划提报量的大小
            // 回复量大于提报量，按提报量向下回复（原计划三旬提报量就是回复量，也就是合同量）
            log.info("dispatchPlanScItemForDepartmentNew_foreach_salesPlanScItemExtendSDOList_scitem_info,分子公司遍历的货品的提报量={},回复量={},任务量={}", monthCount, monthReturnCount,monthCompanyTaskCount);
            String scItemId = salesPlanScItemExtendSDO.getScItemId();
            // 公司级别的每个货品
            // 查询该公司下  该销售组织下的该一个货品   （通过公司查不同组织的占比(策略)，就能拿到组织id，这里遍历数据，取组织id，当然一个组织对应一个占比）
            // ----> 第一步：遍历销售组织，去计算各自的回复量
            Map<String, BigDecimal> orgRatios = Maps.newHashMap();
            // 取到有计划货品的组织列表
            // TODO 销售组织只有一个暂时没有问题，如果有多个销售组织，这个占比计算有问题 ---产品已知该问题
            List<String> orgIds = companyScItemPlanInfo.calculateOrgIdsByScItemId(scItemId);
            for (String orgId : orgIds) {
                Integer ratio = 0;
                for (SalesOrgRatioDTO dto : salesOrgRatioDTOList) {
                    if (orgId.equals(dto.getSalesOrgCode())) {
                        ratio = dto.getRatio();
                    }
                }
                orgRatios.put(orgId, new BigDecimal(ratio));
            }

            SharePie sharePie = new SharePie(orgRatios);
            // 计算出所有组织上中下询的回复量
//                Map</* 组织ID */String, BigDecimal> firstShareRes = sharePie.share(new BigDecimal(monthFirstReturnCount), 0, RoundingMode.DOWN);
//                Map</* 组织ID */String, BigDecimal> middleShareRes = sharePie.share(new BigDecimal(monthMiddleReturnCount), 0, RoundingMode.DOWN);
//                Map</* 组织ID */String, BigDecimal> lastShareRes = sharePie.share(new BigDecimal(monthLastReturnCount), 0, RoundingMode.DOWN);
            Map</* 组织ID */String, BigDecimal> firstShareRes = sharePie.share(new BigDecimal(monthFirstCompanyTaskCount), 0, RoundingMode.DOWN);
            Map</* 组织ID */String, BigDecimal> middleShareRes = sharePie.share(new BigDecimal(monthMiddleCompanyTaskCount), 0, RoundingMode.DOWN);
            Map</* 组织ID */String, BigDecimal> lastShareRes = sharePie.share(new BigDecimal(monthLastCompanyTaskCount), 0, RoundingMode.DOWN);

            // ----> 第二步 遍历部门，去计算各自的回复量
            for (String orgId : orgIds) {
                // 拿到部门策略
                Set<String> withStrategyDepIds = salesPlanStrategyDetailSDOMap.keySet();
                // 有提报数据且有设置策略的部门才参与回复量分配
                List<String> depIds = companyScItemPlanInfo
                        .get(orgId)
                        .calculateDepIdsByScItemId(scItemId, withStrategyDepIds);

                Map</* depId */ String, BigDecimal> firstDepRatios = Maps.newHashMap();
                Map</* depId */ String, BigDecimal> middleDepRatios = Maps.newHashMap();
                Map</* depId */ String, BigDecimal> lastDepRatios = Maps.newHashMap();

                for (String depId : depIds) {
                    SalesPlanStrategyDetailSDO salesPlanStrategyDetailSDO = salesPlanStrategyDetailSDOMap.get(depId);
                    // 拿到当前回复货品的部门计划
                    SalesPlanStatScItemSDO salesPlanStatScItemSDO = companyScItemPlanInfo.get(orgId)
                            .getDepartSalesPlanStatScItemInfoMap().get(depId)
                            .get(scItemId);

                    //如果公司上计划量是0，那么所有部门的上旬计划量一定都是0，部门平分crm回复量
                    if (monthFirstCount == 0){
                        BigDecimal bigDecimal  = firstShareRes.get(orgId);
                        BigDecimal ratio  = bigDecimal.divide(new BigDecimal(depIds.size()),0,RoundingMode.DOWN);
                        firstDepRatios.put(depId, ratio);
                    }else{
                        // dept ratio = 上旬部门提报量 * 部门提报量因子 + 部门合同量 * 部门合同量因子；
                        BigDecimal first = new BigDecimal(salesPlanStrategyDetailSDO.getFirstWeightRatio())
                                .multiply(new BigDecimal(salesPlanStatScItemSDO.getMonthFirstCount()));
                        BigDecimal second = new BigDecimal(salesPlanStrategyDetailSDO.getSecondWeightRatio())
                                .multiply(new BigDecimal(salesPlanStatScItemSDO.getMonthContractCount()));
                        // 理论回复量
                        BigDecimal ratio = first.add(second);
                        firstDepRatios.put(depId, ratio);
                    }

                    //如果公司中旬计划量是0，那么所有部门的中旬计划量一定都是0，部门平分crm回复量
                    if (monthMiddleCount == 0){
                        BigDecimal bigDecimal  = middleShareRes.get(orgId);
                        BigDecimal ratio  = bigDecimal.divide(new BigDecimal(depIds.size()),0,RoundingMode.DOWN);
                        middleDepRatios.put(depId, ratio);
                    }else {
                        // dept ratio = 中旬部门提报量 * 部门提报量因子 + 部门合同量 * 部门合同量因子；
                        BigDecimal first2 = new BigDecimal(salesPlanStrategyDetailSDO.getFirstWeightRatio())
                                .multiply(new BigDecimal(salesPlanStatScItemSDO.getMonthMiddleCount()));
                        BigDecimal second2 = new BigDecimal(salesPlanStrategyDetailSDO.getSecondWeightRatio())
                                .multiply(new BigDecimal(salesPlanStatScItemSDO.getMonthContractCount()));
                        // 理论回复量
                        BigDecimal ratio2 = first2.add(second2);
                        middleDepRatios.put(depId, ratio2);
                    }

                    //如果公司下旬计划量是0，那么所有部门的下旬计划量一定都是0，部门平分crm回复量
                    if (monthLastCount == 0){
                        BigDecimal bigDecimal  = lastShareRes.get(orgId);
                        BigDecimal ratio = bigDecimal.divide(new BigDecimal(depIds.size()),0,RoundingMode.DOWN);
                        lastDepRatios.put(depId, ratio);
                    }else {
                        // dept ratio = 下旬部门提报量 * 部门提报量因子 + 部门合同量 * 部门合同量因子；
                        BigDecimal first3 = new BigDecimal(salesPlanStrategyDetailSDO.getFirstWeightRatio())
                                .multiply(new BigDecimal(salesPlanStatScItemSDO.getMonthLastCount()));
                        BigDecimal second3 = new BigDecimal(salesPlanStrategyDetailSDO.getSecondWeightRatio())
                                .multiply(new BigDecimal(salesPlanStatScItemSDO.getMonthContractCount()));
                        // 理论回复量
                        BigDecimal ratio3 = first3.add(second3);
                        lastDepRatios.put(depId, ratio3);
                    }
                }
                SharePie firstSharePie4Dept = new SharePie(firstDepRatios);
                SharePie middleSharePie4Dept = new SharePie(middleDepRatios);
                SharePie lastSharePie4Dept = new SharePie(lastDepRatios);

                // 各个部门的上中下回复量
                Map</* depId */ String, BigDecimal> firstShareDeptRes = firstSharePie4Dept.share(firstShareRes.get(orgId), 0, RoundingMode.DOWN);
                Map</* depId */ String, BigDecimal> middleShareDeptRes = middleSharePie4Dept.share(middleShareRes.get(orgId), 0, RoundingMode.DOWN);
                Map</* depId */ String, BigDecimal> lastShareDeptRes = lastSharePie4Dept.share(lastShareRes.get(orgId), 0, RoundingMode.DOWN);

                for (String depId : depIds) {
                    // 组装待数据库变更数据
                    SalesPlanScItemExtendSDO newRecord = new SalesPlanScItemExtendSDO();
                    BeanUtils.copyProperties(
                            companyScItemPlanInfo.get(orgId).getDepartSalesPlanStatScItemInfoMap().get(depId).get(scItemId)
                            , newRecord);

                    newRecord.setMonthFirstReturnCount(firstShareDeptRes.get(depId).intValue());
                    newRecord.setMonthFirstTaskCount(firstShareDeptRes.get(depId).intValue());
                    newRecord.setMonthMiddleReturnCount(middleShareDeptRes.get(depId).intValue());
                    newRecord.setMonthMiddleTaskCount(middleShareDeptRes.get(depId).intValue());
                    newRecord.setMonthLastReturnCount(lastShareDeptRes.get(depId).intValue());
                    newRecord.setMonthLastTaskCount(lastShareDeptRes.get(depId).intValue());
                    // 部门任务量
                    newRecord.setTaskCount(newRecord.getMonthFirstTaskCount()
                            + newRecord.getMonthMiddleTaskCount()
                            + newRecord.getMonthLastTaskCount());

                    // 计算旬回复金额
                    newRecord.setMonthFirstReturnMoney(newRecord.getMonthFirstReturnCount() * newRecord.getCostInsuranceFreight());
                    newRecord.setMonthMiddleReturnMoney(newRecord.getMonthMiddleReturnCount() * newRecord.getCostInsuranceFreight());
                    newRecord.setMonthLastReturnMoney(newRecord.getMonthLastReturnCount() * newRecord.getCostInsuranceFreight());

                    //到岸金额
                    newRecord.setTaskOutboundMoney(newRecord.getTaskCount()*newRecord.getOutboundPrice());
                    newRecord.setAdjustOutboundMoney(newRecord.getAdjustCount()*newRecord.getOutboundPrice());

                    // 计算折前任务金额
                    newRecord.setBeforeTaskMoney(newRecord.getTaskCount() * newRecord.getCostInsuranceFreight());
                    // 计算折后任务金额
                    newRecord.calculateTaskMoney(salesPlanSDO.getEstimateDiscount());
                    newRecord.calculateTaskSatisfaction();
                    newRecord.setId(null);
                    newRecord.setModel(SalesPlanModelEnum.PLAN_MODEL.getModel());
                    newRecord.setModelId(planId);
                    // 更新状态为待发布
                    newRecord.setStatus(SalesPlanStatusEnum.TO_BE_PUBLISHED.getCode());
                    planScItemExtendSDOAllList.add(newRecord);
                }
            }
        }
        // 数据库变更提出方法配置事务
        log.info("批量新增生成部门任务量入参{}", JSON.toJSONString(planScItemExtendSDOAllList));
        return doProcessPlanScItemExtend(planId, planScItemExtendSDOAllList);
    }

    @EpochTransactional(appCode="bc_order", dataSourceCode = "cr_ots", dataSourceType = "app")
    public Boolean doProcessPlanScItemExtend(String planId, List<SalesPlanScItemExtendSDO> planScItemExtendSDOAllList) {
        // 最后批量新增
        Boolean aBoolean = salesPlanScItemExtendRepository.batchCreatePlanScItemExtend(planScItemExtendSDOAllList);
        // 更新发布状态为已生成部门任务量
        SalesPlanSDO updateCheckStatusSalesPlanSDO = new SalesPlanSDO();
        updateCheckStatusSalesPlanSDO.setId(Long.valueOf(planId));
        updateCheckStatusSalesPlanSDO.setCheckStatus(SalesPlanCheckStatusEnum.DECOMPOSE_DEPARTMENT_TASK.getCode());
        salesPlanRepository.editSalesPlan(updateCheckStatusSalesPlanSDO);
        return aBoolean;
    }

    /**
     * 查询业务单元下的回复策略
     * @param salesPlanSDO
     * @return
     */
    private SalesPlanStrategyDetailResponse getSalesPlanStrategyDetail(SalesPlanSDO salesPlanSDO){
        QuerySalesPlanStrategyListRequest querySalesPlanStrategyListRequest = new QuerySalesPlanStrategyListRequest();
        querySalesPlanStrategyListRequest.setOrgBusinessCode(salesPlanSDO.getOrgBusinessUnitCode());
        // 目前策略类型都是1(表示计划回复策略)
        querySalesPlanStrategyListRequest.setType(1);
        log.info("dispatchPlanScItemForDepartmentNew_getSalesPlanStrategyDetail_request={}", JSON.toJSONString(querySalesPlanStrategyListRequest));
        Result<SalesPlanStrategyDetailResponse> detailResponseResult =
                salesPlanStrategyDetailQueryService.getSalesPlanStrategyDetail(querySalesPlanStrategyListRequest);
        log.info("dispatchPlanScItemForDepartmentNew_getSalesPlanStrategyDetail_result={}", JSON.toJSONString(detailResponseResult));
        if (null == detailResponseResult || null == detailResponseResult.getResult()) {
            throw new FacadeException("OTS-02-004-01-15-010");
        }
        SalesPlanStrategyDetailResponse salesPlanStrategyDetailResponse = detailResponseResult.getResult();
        // 组织占比策略
        List<SalesOrgRatioDTO> salesOrgRatioDTOList = salesPlanStrategyDetailResponse.getSalesOrgRatioDTOList();
        if (CollectionUtils.isEmpty(salesOrgRatioDTOList)) {
            throw new FacadeException("OTS-02-004-01-15-011");
        }
        // 部门占比策略
        if (CollectionUtils.isEmpty(salesPlanStrategyDetailResponse.getSalesPlanStrategyDetailDepartmentList())) {
            throw new FacadeException("OTS-02-004-01-15-012");
        }
        return salesPlanStrategyDetailResponse;
    }

    /**
     *
     * @param planId          计划id
     * @param salesOrgRatioDTOList     组织策略列表
     * @param salesPlanStrategyDetailResponse  策略详情
     * @return
     */
    private CompanyScItemPlanInfo getCompanyScItemPlanInfo(String planId, List<SalesOrgRatioDTO> salesOrgRatioDTOList, SalesPlanStrategyDetailResponse salesPlanStrategyDetailResponse){
        Map<String,OrgScItemPlanInfo> map = Maps.newHashMap();
        // 遍历配置策略的销售组织，去计算各自的占比
        for (SalesOrgRatioDTO salesOrgRatioDTO : salesOrgRatioDTOList) {
            // 根据策略获取组织id
            String organizationId = salesOrgRatioDTO.getSalesOrgCode();
            // 查询该组织下的所有部门, 方法一
            /*QuerySpecifiedDepthRepoRequest querySpecifiedDepthRepoRequest = new QuerySpecifiedDepthRepoRequest();
            querySpecifiedDepthRepoRequest.setOrganizationId(organizationId);
            querySpecifiedDepthRepoRequest.setOrganizationType(OrganizationTypeEnum.SALES_DEPARTMENT.getCode());
            List<OrganizationSDO> departmentSDOForOrg = organizationRepository.querySpecifiedDepth(querySpecifiedDepthRepoRequest);*/
            // 查询销售组织下的所有部门， 方法二，可以避免内部循环rpc
            IdQueryRequest idQueryRequest = new IdQueryRequest();
            idQueryRequest.setId(organizationId);
            idQueryRequest.setType(OrganizationTypeEnum.SALES_DEPARTMENT.getCode());
            Result<List<OrganizationSDO>> organizationResult = organizationQueryService.getDownQueryOrganization(idQueryRequest);
            if (null == organizationResult || CollectionUtils.isEmpty(organizationResult.getResult())) {
                log.error("SalesPlanPublishWriteServiceNewImpl_getCompanyScItemPlanInfo_error,销售组织:{}未配置部门", organizationId);
                continue;
            }
            OrgScItemPlanInfo orgScItemPlanInfo = new OrgScItemPlanInfo();
            orgScItemPlanInfo.setOrganizationId(organizationId);
            orgScItemPlanInfo.setOrganizationSDOS(organizationResult.getResult());

            // 查询组织下的所有计划货品
            // 西安的部门货品达到600，上海的部门货品量超过千
            fetchPlanScItemForSaleOrg(planId, organizationId, orgScItemPlanInfo);

            // 查询该组织下所有部门的计划货品
            // 拿到部门策略
            List<SalesPlanStrategyDetailSDO> salesPlanStrategyDetailDepartmentList = salesPlanStrategyDetailResponse.getSalesPlanStrategyDetailDepartmentList();
            List<String> departmentIdList = salesPlanStrategyDetailDepartmentList.stream().map(SalesPlanStrategyDetailSDO::getReplyTargetId).collect(Collectors.toList());
            Map<String, List<SalesPlanStatScItemSDO>> resultMap = fetchPlanScItemForDepartment(planId, departmentIdList);
            if (MapUtils.isEmpty(resultMap)) {
                log.error("SalesPlanPublishWriteServiceNewImpl_getCompanyScItemPlanInfo_error,配置的策略部门下没有计划货品");
                throw new FacadeException("OTS-02-004-01-15-013");
            }

            Map</* departmentId */ String , Map</* scItemId */ String, SalesPlanStatScItemSDO>> departSalesPlanStatScItemInfoMap = Maps.newHashMap();
            // 先遍历部门策略，然后将有计划货品的部门塞进去
            for (String departmentId: departmentIdList){
                List<SalesPlanStatScItemSDO> salesPlanStatScItemSDO = resultMap.get(departmentId);
                if(CollectionUtils.isNotEmpty(salesPlanStatScItemSDO)) {
                    Map</* scItemId */ String, SalesPlanStatScItemSDO> collect = salesPlanStatScItemSDO.stream().collect(Collectors.toMap(SalesPlanStatScItemSDO::getScItemId, a -> a));
                    departSalesPlanStatScItemInfoMap.put(departmentId, collect);
                }
            }
            orgScItemPlanInfo.setDepartSalesPlanStatScItemInfoMap(departSalesPlanStatScItemInfoMap);
            map.put(organizationId, orgScItemPlanInfo);
        }

        CompanyScItemPlanInfo companyScItemPlanInfo = new CompanyScItemPlanInfo();
        companyScItemPlanInfo.setCompanyScItemInfo(map);
        return companyScItemPlanInfo;
    }

    private void fetchPlanScItemForSaleOrg(String planId, String organizationId, OrgScItemPlanInfo orgScItemPlanInfo) {
        int page = 1;
        List<SalesPlanStatScItemSDO> salesPlanStatScItemSDOS = Lists.newArrayList();
        PageResult<List<SalesPlanStatScItemSDO>> pageResult = fetchPlanScItemListForSalesOrg(planId, organizationId, page);
        while (null != pageResult) {
            List<SalesPlanStatScItemSDO> salesPlanStatScItemOrganizationSDOList = pageResult.getContent();
            salesPlanStatScItemSDOS.addAll(salesPlanStatScItemOrganizationSDOList);
            // 已经是最后一页
            if (pageResult.getContent().size() < DEFAULT_PAGE_SIZE) {
                break;
            }
            pageResult = fetchPlanScItemListForSalesOrg(planId, organizationId, ++page);
        }
        if (CollectionUtils.isNotEmpty(salesPlanStatScItemSDOS)) {
            // 构造组织计划货品map
            orgScItemPlanInfo.setOrgSalesPlanStatScItemInfoMap(salesPlanStatScItemSDOS
                    .stream().collect(Collectors.toMap(SalesPlanStatScItemSDO::getScItemId, item -> item,(v1,v2)->v1)));
        }
    }

    private Map<String, List<SalesPlanStatScItemSDO>> fetchPlanScItemForDepartment(String planId, List<String> departmentIdList) {
        int page = 1;
        List<SalesPlanStatScItemSDO> salesPlanStatScItemSDOS = Lists.newArrayList();
        PageResult<List<SalesPlanStatScItemSDO>> pageResult = fetchPlanScItemListForDepartment(planId, page, departmentIdList);
        while (null != pageResult) {
            List<SalesPlanStatScItemSDO> salesPlanStatScItemSDOList = pageResult.getContent();
            salesPlanStatScItemSDOS.addAll(salesPlanStatScItemSDOList);
            // 已经是最后一页
            if (pageResult.getContent().size() < DEFAULT_PAGE_SIZE) {
                break;
            }
            pageResult = fetchPlanScItemListForDepartment(planId, ++page, departmentIdList);
        }
        Map</* departmentId */ String, List<SalesPlanStatScItemSDO>> resultMap = Maps.newHashMap();
        if (CollectionUtils.isNotEmpty(salesPlanStatScItemSDOS)) {
            // key :departmentId  value:SalesPlanStatScItemSDO
            resultMap = salesPlanStatScItemSDOS.stream().collect(Collectors.toMap(SalesPlanStatScItemSDO::getBizId, Lists::newArrayList,(a, b)->{
                a.addAll(b);
                return a;
            }));
        }
        return resultMap;
    }

    private PageResult<List<SalesPlanStatScItemSDO>> fetchPlanScItemListForDepartment(String planId, Integer page, List<String> departmentIdList) {
        // 批量查询部门的计划货品数量
        QueryPlanScItemStatRequest queryPlanScItemStatRequestForDepartment = new QueryPlanScItemStatRequest();
        queryPlanScItemStatRequestForDepartment.setPlanId(Long.valueOf(planId));
        queryPlanScItemStatRequestForDepartment.setBizType(SalesStatBizTypeEnum.SALES_DEPARTMENT.getBizType());
        queryPlanScItemStatRequestForDepartment.setPoolType(Objects.toString(StatPoolTypeEnum.SC_ITEM.getPoolType()));
        queryPlanScItemStatRequestForDepartment.setBizIds(departmentIdList);
        queryPlanScItemStatRequestForDepartment.setSize(DEFAULT_PAGE_SIZE);
        queryPlanScItemStatRequestForDepartment.setPage(page);
        log.info("查询分子公司下配置策略的所有部门的汇总数据参数{}", JSON.toJSONString(queryPlanScItemStatRequestForDepartment));
        PageResult<List<SalesPlanStatScItemSDO>> pageResult = salesPlanScItemStatRepository.batchQueryPlanScItemByBizId(queryPlanScItemStatRequestForDepartment);
        log.info("查询分子公司下配置策略的所有部门的汇总数据结果{}", JSON.toJSONString(pageResult));
        if (null == pageResult || CollectionUtils.isEmpty(pageResult.getContent())) {
            return null;
        }
        return pageResult;
    }

    private PageResult<List<SalesPlanStatScItemSDO>> fetchPlanScItemListForSalesOrg(String planId, String organizationId, Integer page) {
        QueryPlanScItemStatRequest queryPlanScItemStatRequest = new QueryPlanScItemStatRequest();
        queryPlanScItemStatRequest.setPlanId(Long.valueOf(planId));
        queryPlanScItemStatRequest.setBizType(SalesStatBizTypeEnum.SALES_ORGANIZATION.getBizType());
        queryPlanScItemStatRequest.setPoolType(Objects.toString(StatPoolTypeEnum.SC_ITEM.getPoolType()));
        queryPlanScItemStatRequest.setBizId(organizationId);
        queryPlanScItemStatRequest.setPage(page);
        queryPlanScItemStatRequest.setSize(DEFAULT_PAGE_SIZE);
        // 该组织下所有计划货品数据
        log.info("查询分子公司下组织的汇总数据参数{}", JSON.toJSONString(queryPlanScItemStatRequest));
        PageResult<List<SalesPlanStatScItemSDO>> pageResult = salesPlanScItemStatRepository.batchQueryPlanScItemByBizId(queryPlanScItemStatRequest);
        log.info("查询分子公司下组织的汇总数据结果{}", JSON.toJSONString(pageResult));
        if (null == pageResult || CollectionUtils.isEmpty(pageResult.getContent())) {
            return null;
        }
        return pageResult;
    }

    private void checkParam(DispatchPlanScItemRequest dispatchPlanScItemRequest) {
        if (StringUtils.isBlank(dispatchPlanScItemRequest.getPlanId())) {
            throw new ActivityException("OTS-02-004-01-15-001");
        }
    }

    /**
     * 校验计划
     *
     * @param planId
     * @return
     */
    private SalesPlanSDO checkPlan(String planId) {
        QuerySalesPlanRequest querySalesPlanRequest = new QuerySalesPlanRequest();
        querySalesPlanRequest.setId(planId);
        SalesPlanSDO salesPlanSDO = salesPlanRepository.load(querySalesPlanRequest);
        if (Objects.isNull(salesPlanSDO)) {
            throw new FacadeException("OTS-02-002-00-16-001");
        }
//        Integer status = salesPlanSDO.getStatus();
//        if (!SalesPlanStatusEnum.TO_BE_PUBLISHED.getCode().equals(status)) {
//            throw new FacadeException("OTS-02-004-01-15-004");
//        }
        return salesPlanSDO;
    }

    /**
     * 计算上中下询的客户调整明细   （客户调整量为负值，默认回复）
     *
     * @param monthAdjustCount             各三询的调整提报量
     * @param adjustPositiveSum            各三询的调整提报量正值汇总
     * @param monthAdjustTheoryReturnCount 业务单元的三旬实际回复值
     * @return
     */
    private Integer getMonthAdjustReturnCountDefaultNegative(Integer monthAdjustCount, Integer adjustPositiveSum, Integer monthAdjustTheoryReturnCount) {

        Integer monthFirstAdjustReturnCount;
        // 判断调整提报量的正负
        // 调整提报量为负，按原值优先回复
        if (monthAdjustCount <= 0) {
            monthFirstAdjustReturnCount = monthAdjustCount;
        }
        // 上旬调整提报量是正值
        else {
            //计算上旬理论回复占比  客户货品调整量正值/客户货品调整量正值的汇总
            BigDecimal bigDecimal = CalculatePercentage.retainTwo(monthAdjustCount.longValue(), adjustPositiveSum.longValue());
            BigDecimal adjustRate = bigDecimal.multiply(new BigDecimal("0.01")).setScale(2, RoundingMode.HALF_UP);
            //计算上旬客户理论回复量   业务单元实际回复量*理论回复占比
            monthFirstAdjustReturnCount = new BigDecimal(monthAdjustTheoryReturnCount).multiply(adjustRate).setScale(0, RoundingMode.HALF_UP).intValue();
        }
        return monthFirstAdjustReturnCount;
    }

    /**
     * 计算上中下询的客户调整明细   （客户调整量为正值，默认回复）
     *
     * @param monthAdjustCount             各三询的调整提报量
     * @param adjustNegativeSum            各三询的调整提报量负值汇总
     * @param monthAdjustTheoryReturnCount 业务单元的三旬实际回复值
     * @return
     */
    private Integer getMonthAdjustReturnCountDefaultPositive(Integer monthAdjustCount, Integer adjustNegativeSum, Integer monthAdjustTheoryReturnCount) {

        int monthFirstAdjustReturnCount;
        // 判断调整提报量的正负
        // 调整提报量为负
        if (monthAdjustCount <= 0) {
            //计算上旬理论回复占比  客户货品调整量正值/客户货品调整量正值的汇总
            BigDecimal bigDecimal = CalculatePercentage.retainTwo(monthAdjustCount.longValue(), adjustNegativeSum.longValue());
            BigDecimal adjustRate = bigDecimal.multiply(new BigDecimal("0.01")).setScale(2, RoundingMode.HALF_UP);
            //计算上旬客户理论回复量   业务单元实际回复量*理论回复占比
            monthFirstAdjustReturnCount = new BigDecimal(monthAdjustTheoryReturnCount).multiply(adjustRate).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();

        }
        // 上旬调整提报量是正值，按原值默认回复
        else {
            monthFirstAdjustReturnCount = monthAdjustCount;
        }
        return monthFirstAdjustReturnCount;
    }

}
